#include "libMontador.h"

/*
 * Alimenta a matriz de erro no índice contador de erros.
 * Recebe como parâmetro uma mensagem e o número da linha do arquivo.
 */
void armazenaErro(char * mensagem, int linha) {
    if (errorCounter <= ERRORS_LIMIT) {
        TabErros[errorCounter].linha = linha;
        strcpy(TabErros[errorCounter].mensagem, mensagem);
        errorCounter++;
    }
}

/*
 * Realiza uma busca na tabela de instruções pelo token informado.
 * Retorna o índice da matriz caso encontre a instrução cc retorna falso.
 */
int buscaInstrucao(char * token) {
    int i;
    for (i = 0; i < NUM_INSTRUCOES; i++) {
        if (!strcmp(token, TabInstrucoes[i].nome)) {
            return i;
        }
    }
    return FALSE;
}

/*
 * Realiza uma busca na matriz de símbolos pelo símbolo informado.
 * Retorna true caso encontrado ou false cc.
 */
int buscaSimbolo(char * simbolo) {
    int i;
    for (i = 0; i < sybolCounter; i++) {
        if (!strcmp(simbolo, MatrizSimbolos[i].simbolo)) {
            return i;
        }
    }
    return FALSE;
}

/*
 * Percorre a string 'texto' passada e conta a ocorrência do caractere informado.
 * Retorna o número de ocorrências.
 */
int contaOcorrencia(char * texto, char caractere) {
    int i, quant = 0;
    // vamos percorrer todos os caracteres da string
    for (i = 0; texto[i]; i++) {
        if (texto[i] == caractere) { // localizamos
            quant++;
        }
    }
    return quant;
}

int criaArquivoBin() {

    FILE * arquivoBin;
    char caminhoAbsolutoBin[255] = {'\0'};

    strcat(caminhoAbsolutoBin, caminhoArquivo);
    strcat(caminhoAbsolutoBin, nomeArquivo);
    strcat(caminhoAbsolutoBin, ".bin");

    //Criação do arquivo binário
    arquivoBin = fopen(caminhoAbsolutoBin, "wb");

    //Verificação do sucesso da criação do arquivo binário
    if (arquivoBin == NULL) {
        return FALSE;
    }

    FILE * arquivoRealoc;
    char caminhoAbsolutoRealoc[255] = {'\0'};

    strcat(caminhoAbsolutoRealoc, caminhoArquivo);
    strcat(caminhoAbsolutoRealoc, nomeArquivo);
    strcat(caminhoAbsolutoRealoc, ".realoc");

    //Criação do arquivo binário
    arquivoRealoc = fopen(caminhoAbsolutoRealoc, "wb");

    //Verificação do sucesso da criação do arquivo binário
    if (arquivoRealoc == NULL) {
        return FALSE;
    }

    //Buffer de leitura
    int i;
    //Conjunto de dados (simula o arquivo com os opcodes)

    //Posicionando os ponteiros de arquivo no ínicio do arquivo
    fseek(arquivoBin, 0, SEEK_SET);

    for (i = 0; i < programCounter; i++) {
        fwrite(&MEM[i], sizeof (short int), 1, arquivoBin);
    }

    //Fechamento dos arquivos
    fclose(arquivoBin);

    fseek(arquivoRealoc, 0, SEEK_SET);

    for (i = 0; i < programCounter; i++) {
        fwrite(&REALOC[i], sizeof (short int), 1, arquivoRealoc);
    }

    return TRUE;
}

/*
 * Finaliza o programa com a condição de saída passada.
 * É impressa a matriz de erros antes da finalização.
 */
void finalizaPrograma(int condicaoDeSaida) {
    int i;
    for (i = 0; i < errorCounter; i++) {
        printf("[ERRO]:: linha %d :: %s!\n", TabErros[i].linha, TabErros[i].mensagem);
    }
    exit(condicaoDeSaida);
}

/*
 * Grava o endereço passado na posiçao atual do vetor de saída
 */
void gravaEndereco(char * endereco) {
    strcpy(VetSaida[programCounter].saida, endereco);
    REALOC[programCounter] = TRUE;
    programCounter++;
}

/*
 * Grava o código da instrução passado como parâmetro na posição atual do vetor de saída.
 * Antes é convertido o código da instrução em string para ser aceito no vetor.
 */
void gravaInstrucao(int indiceInstrucao) {
    if (TabInstrucoes[indiceInstrucao].codigo != TabInstrucoes[ATRIB].codigo) {
        if (TabInstrucoes[indiceInstrucao].codigo != TabInstrucoes[SPACE].codigo) {
            char buffer[255] = {'\0'};
            sprintf(buffer, "%d", TabInstrucoes[indiceInstrucao].codigo);
            strcpy(VetSaida[programCounter].saida, buffer);
        }
        programCounter++;
    }
}

/*
 * Grava o símbolo informado na matriz de símbolos.
 * É o caso em que o label é uma definição ou declaração.
 * Antes de gravar percorre a matriz até o ponto atual a fim de verificar se o label já não foi gravado.
 * Se já foi gravado é caracterizado uma dublicidadade de label.
 * Retorna ture ou false caso a gravação tenha sido efetuada ou não.
 */
int gravaSimboloMatriz(char * simbolo) {
    if (buscaSimbolo(simbolo) == FALSE) {
        MatrizSimbolos[sybolCounter].endereco = (short int) programCounter;
        REALOC[programCounter] = TRUE;
        strcpy(MatrizSimbolos[sybolCounter].simbolo, simbolo);
        sybolCounter++;
        return TRUE;
    }
    return FALSE;
}

/*
 * Grava o label passado na posição atual do vetor de saída
 */
void gravaSimboloSaida(char * simbolo) {
    strcpy(VetSaida[programCounter].saida, simbolo);
    REALOC[programCounter] = TRUE;
    programCounter++;
}

/*
 * Inicializa a matriz de instruções com o minemônico e o código de cada instrução.
 * Adicionado a diretiva SPACE que serve para alocação de espaço em memória para uma variável.
 */
void inicializaTabInstrucoes() {

    strcpy(TabInstrucoes[0].nome, "ADD");
    TabInstrucoes[0].codigo = 2;
    TabInstrucoes[0].operandos = 1;

    strcpy(TabInstrucoes[1].nome, "BR");
    TabInstrucoes[1].codigo = 0;
    TabInstrucoes[1].operandos = 1;

    strcpy(TabInstrucoes[2].nome, "BRNEG");
    TabInstrucoes[2].codigo = 5;
    TabInstrucoes[2].operandos = 1;

    strcpy(TabInstrucoes[3].nome, "BRPOS");
    TabInstrucoes[3].codigo = 1;
    TabInstrucoes[3].operandos = 1;

    strcpy(TabInstrucoes[4].nome, "BRZERO");
    TabInstrucoes[4].codigo = 4;
    TabInstrucoes[4].operandos = 1;

    strcpy(TabInstrucoes[5].nome, "COPY");
    TabInstrucoes[5].codigo = 13;
    TabInstrucoes[5].operandos = 2;

    strcpy(TabInstrucoes[6].nome, "DIVIDE");
    TabInstrucoes[6].codigo = 10;
    TabInstrucoes[6].operandos = 1;

    strcpy(TabInstrucoes[7].nome, "LOAD");
    TabInstrucoes[7].codigo = 3;
    TabInstrucoes[7].operandos = 1;

    strcpy(TabInstrucoes[8].nome, "MULT");
    TabInstrucoes[8].codigo = 14;
    TabInstrucoes[8].operandos = 1;

    strcpy(TabInstrucoes[9].nome, "READ");
    TabInstrucoes[9].codigo = 12;
    TabInstrucoes[9].operandos = 1;

    strcpy(TabInstrucoes[10].nome, "STOP");
    TabInstrucoes[10].codigo = 11;
    TabInstrucoes[10].operandos = 0;

    strcpy(TabInstrucoes[11].nome, "STORE");
    TabInstrucoes[11].codigo = 7;
    TabInstrucoes[11].operandos = 1;

    strcpy(TabInstrucoes[12].nome, "SUB");
    TabInstrucoes[12].codigo = 6;
    TabInstrucoes[12].operandos = 1;

    strcpy(TabInstrucoes[13].nome, "WRITE");
    TabInstrucoes[13].codigo = 8;
    TabInstrucoes[13].operandos = 1;

    strcpy(TabInstrucoes[14].nome, "SPACE");
    TabInstrucoes[14].codigo = 9;
    TabInstrucoes[14].operandos = 0;

    strcpy(TabInstrucoes[15].nome, "ATRIB");
    TabInstrucoes[15].codigo = 80;
    TabInstrucoes[15].operandos = 1;

}

/*
 * Verifica se a string passa é um número.
 */
int isNumber(char * string) {
    int i;
    for (i = 0; string[i]; i++) {
        if (!(string[i] >= '0' && string[i] <= '9')) {
            return FALSE;
        }
    }
    return TRUE;
}

/*
 * Responsável pela abertura do arquivo.
 * Caso o arquivo exista retorna um ponteiro para o mesmo.
 */
FILE * openFile() {
    char strArq[255];
    FILE * arquivo;
    printf("Informe o arquivo a ser lido: ");
    scanf("%s", strArq);
    getchar();

    trataCaminhoArquivo(strArq);

    if ((arquivo = fopen(strArq, "r")) == NULL) {
        printf("O arquivo especificado não existe!\n");
        exit(EXIT_FAILURE);
    }

    return arquivo;
}

/*
 * Percorre a string 'texto' removendo o caractere informado.
 * Retorna uma nova string 'limpa'.
 */
char * removeChar(char * texto, char caractere) {
    char * temp = malloc(sizeof (char) * strlen(texto));
    int i;

    for (i = 0; texto[i] != '\n'; i++) {
        if (texto[i] != caractere) {
            temp[i] = texto[i];
        }
    }
    return temp;
}

void segundaPassagem() {
    int i;
    int indiceSimbolo = FALSE;
    for (i = 0; i < programCounter; i++) {
        if (isNumber(VetSaida[i].saida) == TRUE) {
            MEM[i] = (short int) atoi(VetSaida[i].saida);
        } else {
            indiceSimbolo = buscaSimbolo(VetSaida[i].saida);
            if (indiceSimbolo == FALSE) {
                armazenaErro("Label não declarado", i);
            } else {
                MEM[i] = MatrizSimbolos[indiceSimbolo].endereco;
            }
        }
    }
}

/*
 * Converte todos os caracteres da string passada para UPERCASE
 */
char * strToUpper(char * str) {
    int i;
    for (i = 0; str[i]; i++) {
        str[i] = toupper(str[i]);
    }
    return str;
}

/*
 * Realiza o tratamento da linha do arquivo.
 * Remove o \n do fim da linha.
 * Conta o número de tokens da linha.
 * Quebra a linha em tokens a partir do espaço em branco.
 * Retorna um ponteiro com os tokens da linha.
 * O Retorno NULL caracteriza um erro.
 */
char * trataLinha(char * linha) {
    linha = removeChar(linha, '\n');
    if (contaOcorrencia(linha, ' ') > LIM_TOKEN) {
        return NULL;
    }
    char * tokens = NULL;
    tokens = strtok(linha, " ");

    return tokens;
}

void trataCaminhoArquivo(char * caminhoArq) {

    int fim = strlen(caminhoArq);
    int iExtencao, iBarra, i = 0;
    for (iExtencao = fim; caminhoArq[iExtencao] != '.'; --iExtencao);
    for (iBarra = fim; caminhoArq[iBarra] != '/'; --iBarra);
    for (fim = iBarra + 1; fim < iExtencao; fim++, i++) {
        nomeArquivo[i] = caminhoArq[fim];
    }
    for (i = 0; i <= iBarra; i++) {
        caminhoArquivo[i] = caminhoArq[i];
    }
}